package com.cwm.test.proxy.autocode.proxy;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class GPProxy {


    public  Object newProxyInstance(GPClassloader loader, Class<?>[] interfaces, GPInvocationHandler h){

        try {
            // 1、动态生产源码 .java 文件
             String src  = generateSrc(interfaces);
            // 2、java文件输出到磁盘，保存为文件 $Proxy0.java
            String filePath = GPProxy.class.getResource("").getPath();
            File file = new File(filePath+"$Proxy1.java");
            FileWriter fw = null;
            fw = new FileWriter(file);
            fw.write(src);
            fw.flush();
            fw.close();
            // 3、把.java文件编译成$Proxy0.class文件
            JavaCompiler jc = ToolProvider.getSystemJavaCompiler();
            StandardJavaFileManager sjfm = jc.getStandardFileManager(null,null,null);
            Iterable iterable =sjfm.getJavaFileObjects(file);
            JavaCompiler.CompilationTask task = jc.getTask(null,sjfm,null,null,null,iterable);
            task.call();
            sjfm.close();
            //4、把生产的.class文件加载到jvm中
            Class proxyClass = loader.findClass("$Proxy1");
            Constructor constructor = proxyClass.getConstructor(GPInvocationHandler.class);

            //5、字节码重组，返回新的代码对象
            return constructor.newInstance(h);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private  String generateSrc(Class<?>[] interfaces) {
        StringBuilder sb = new StringBuilder();
        sb.append("package com.cwm.test.proxy.autocode.proxy;\n" +
                "\n" +
                "import com.cwm.test.proxy.autocode.client.Source;\n" +
                "\n" +
                "import java.lang.reflect.*;\n" +
                "\n" +
                "/**\n" +
                " * * 动态代理类\n" +
                " */\n" +
                "public class $Proxy1 extends GPProxy implements Source {\n" +
                "    GPInvocationHandler h;\n" +
                "\n" +
                "    public $Proxy1(GPInvocationHandler invocationhandler) {\n" +
                "        this.h = invocationhandler;\n" +
                "    }\n" +
                "\n" +
                "    public boolean equals(Object obj) {\n" +
                "        try {\n" +
                "            return ((Boolean) h.invoke(this, m1, new Object[]{\n" +
                "                    obj\n" +
                "            })).booleanValue();\n" +
                "        } catch (Throwable throwable) {\n" +
                "            throw new UndeclaredThrowableException(throwable);\n" +
                "        }\n" +
                "    }\n" +
                "\n" +
                "    public String toString() {\n" +
                "        try {\n" +
                "            return (String) h.invoke(this, m2, null);\n" +
                "        } catch (Throwable throwable) {\n" +
                "            throw new UndeclaredThrowableException(throwable);\n" +
                "        }\n" +
                "    }\n" +
                "\n" +
                "    public int mul(int i, int j) {\n" +
                "        try {\n" +
                "            return ((Integer) h.invoke(this, m6, new Object[]{\n" +
                "                    Integer.valueOf(i), Integer.valueOf(j)\n" +
                "            })).intValue();\n" +
                "        } catch (Throwable throwable) {\n" +
                "            throw new UndeclaredThrowableException(throwable);\n" +
                "        }\n" +
                "    }\n" +
                "\n" +
                "    public int add(int i, int j) {\n" +
                "        try {\n" +
                "            return ((Integer) h.invoke(this, m3, new Object[]{\n" +
                "                    Integer.valueOf(i), Integer.valueOf(j)\n" +
                "            })).intValue();\n" +
                "        } catch (Throwable throwable) {\n" +
                "            throw new UndeclaredThrowableException(throwable);\n" +
                "        }\n" +
                "    }\n" +
                "\n" +
                "    public int sub(int i, int j) {\n" +
                "        try {\n" +
                "            return ((Integer) h.invoke(this, m5, new Object[]{\n" +
                "                    Integer.valueOf(i), Integer.valueOf(j)\n" +
                "            })).intValue();\n" +
                "        } catch (Throwable throwable) {\n" +
                "            throw new UndeclaredThrowableException(throwable);\n" +
                "        }\n" +
                "    }\n" +
                "\n" +
                "    public int div(int i, int j) {\n" +
                "        try {\n" +
                "            return ((Integer) h.invoke(this, m4, new Object[]{\n" +
                "                    Integer.valueOf(i), Integer.valueOf(j)\n" +
                "            })).intValue();\n" +
                "        } catch (Throwable throwable) {\n" +
                "            throw new UndeclaredThrowableException(throwable);\n" +
                "        }\n" +
                "    }\n" +
                "\n" +
                "    public int hashCode() {\n" +
                "        try {\n" +
                "            return ((Integer) h.invoke(this, m0, null)).intValue();\n" +
                "        } catch (Throwable throwable) {\n" +
                "            throw new UndeclaredThrowableException(throwable);\n" +
                "        }\n" +
                "    }\n" +
                "\n" +
                "    private static Method m1;\n" +
                "    private static Method m2;\n" +
                "    private static Method m6;\n" +
                "    private static Method m3;\n" +
                "    private static Method m5;\n" +
                "    private static Method m4;\n" +
                "    private static Method m0;\n" +
                "\n" +
                "    static {\n" +
                "        try {\n" +
                "            m1 = Class.forName(\"java.lang.Object\").getMethod(\"equals\", new Class[]{\n" +
                "                    Class.forName(\"java.lang.Object\")\n" +
                "            });\n" +
                "            m2 = Class.forName(\"java.lang.Object\").getMethod(\"toString\", new Class[0]);\n" +
                "            m6 = Class.forName(\"com.cwm.test.proxy.autocode.client.Source\").getMethod(\"mul\", new Class[]{\n" +
                "                    Integer.TYPE, Integer.TYPE\n" +
                "            });\n" +
                "            m3 = Class.forName(\"com.cwm.test.proxy.autocode.client.Source\").getMethod(\"add\", new Class[]{\n" +
                "                    Integer.TYPE, Integer.TYPE\n" +
                "            });\n" +
                "            m5 = Class.forName(\"com.cwm.test.proxy.autocode.client.Source\").getMethod(\"sub\", new Class[]{\n" +
                "                    Integer.TYPE, Integer.TYPE\n" +
                "            });\n" +
                "            m4 = Class.forName(\"com.cwm.test.proxy.autocode.client.Source\").getMethod(\"div\", new Class[]{\n" +
                "                    Integer.TYPE, Integer.TYPE\n" +
                "            });\n" +
                "            m0 = Class.forName(\"java.lang.Object\").getMethod(\"hashCode\", new Class[0]);\n" +
                "        } catch (NoSuchMethodException nosuchmethodexception) {\n" +
                "            throw new NoSuchMethodError(nosuchmethodexception.getMessage());\n" +
                "        } catch (ClassNotFoundException classnotfoundexception) {\n" +
                "            throw new NoClassDefFoundError(classnotfoundexception.getMessage());\n" +
                "        }\n" +
                "    }\n" +
                "}\n");
        return sb.toString();
    }

}
